﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Windows.Forms;
using Viper;
using GEX3;
using System.Threading;
using System.Runtime.InteropServices;
namespace viperv3
{
    public class call
    {
        public uint address;
        public uint instancePos;
        public uint returnaddress;
        
        public uint ClassAddress;
    }

    public class ViperVirtualMachine
    {
        public Local[] locals = new Local[1000];
        private class field
        {
            public string Name;
            public object Value;
        }
        public Stack<object> Stack = new Stack<object>();
        public Stack<call> CallStack = new Stack<call>();
        public Buffer memory = new Buffer(2000);
        bool thrown = false;
        System.Windows.Forms.Form frm;
        public Stack<uint> Exceptionstack = new Stack<uint>();
        public void CALL(uint addr,string C = "::..")
        {
            call n = new call();
            n.returnaddress = memory.pos;
            n.address = addr;
            
            
            foreach (classInformation cI in info)
            {
                if (cI.Name == C)
                {
                    n.instancePos = (uint)cI.Address;
                }
            }
            foreach (Instance i in InstanceInfo)
            {
                if (i.Name == C)
                {
                    n.instancePos = (uint)i.Address;
                }
            }
            CallStack.Push(n);
            memory.pos = addr;

        }
        void create()
        {
            System.Windows.Forms.Application.Run(frm);
        }
        string msg = "";
        int vstart = 0;
        byte bad_oc = 0;
        public int vi = 0;
        void click(object sender, EventArgs e)
        {
            foreach (Control c in frm.Controls)
            {
                if (((Control)sender).Name == c.Name)
                {
                    c.Tag = 1;
                }
            }
        }
        int interceptMessage(string name)
        {
            foreach (Control c in frm.Controls)
            {
                if (c.Name == name)
                {
                   // Console.WriteLine(c.Tag.ToString());
                    int i = (int)c.Tag;
                    c.Tag = 0;
                    return i;
                }
            }
            return 0;
        }
        class Instance
        {
            public string Name;
            public string BaseClass;
            public int Address;
            public List<field> fields = new List<field>();
        }
        public List<vClass> LoadedClasses = new List<vClass>();
        public bool redirectOutput = false;
        public string Output = "";
        public delegate void NextOpcodeChange();
        public event NextOpcodeChange nextOpcode;
        public delegate void refresh();
        public event refresh Refresh;
        public string Disassemble()
        {
            string Ret = "";
            uint oldPos = memory.pos;
            byte opcode = memory.Read();
            if (opcode == Opcodes.SysfPrint)
            {
                Ret = "sysf_print";
            }
            else if (opcode == Opcodes.PushString)
            {
                Ret += "push string '" + memory.ReadString() + "'";
            }
            else if (opcode == Opcodes.Add)
            {
                Ret = "add";
            }
            else if (opcode == Opcodes.Bgt)
            {
                Ret = "bgt 0x" + memory.ReadInt32().ToString("X");
            }
            else if (opcode == Opcodes.Bne)
            {
                Ret += "bne 0x" + memory.ReadInt32().ToString("X");
            }
            else if (opcode == Opcodes.pushdword)
            {
                Ret += "push dword 0x" + memory.ReadInt32().ToString("X");
            }
            else if (opcode == Opcodes.Stloc)
            {
                Ret += "stloc " + memory.ReadInt32().ToString();
            }
            else if (opcode == Opcodes.Bre)
            {
                Ret += "bre 0x" + memory.ReadInt32().ToString("X");
            }
            else if (opcode == Opcodes.Add)
            {
                Ret = "add";

            }
            else if (opcode == Opcodes.Div)
            {
                Ret = "div";
            }
            else if (opcode == Opcodes.Get)
            {
                string inst = memory.ReadString();
                string name = memory.ReadString();
                Ret += "get " + inst + "::" + name;
            }
            else if (opcode == Opcodes.Set)
            {
                string inst = memory.ReadString();
                string name = memory.ReadString();
                Ret += "get " + inst + "::" + name;
            }
            else if (opcode == Opcodes.Halt)
            {
                Ret += "halt";
            }
            else if (opcode == Opcodes.Jmp)
            {
                Ret += "jmp 0x" + memory.ReadInt32().ToString("X");
            }
            else if (opcode == Opcodes.Ldloc)
            {
                Ret += "ldloc " + memory.ReadInt32().ToString();
            }
            else if (opcode == Opcodes.Mul)
            {
                Ret += "mul";
            }
            else
            {
                Ret += "IL_" + opcode.ToString();
            }
            memory.pos = oldPos;
            return Ret;
        }
        public bool Debug = false;
        public int Instruction;
        public int InstructionPointer;
        public string InstructionDA = "";
        public class Local
        {
            public object Datatype;
            public int address;
        }
        public bool ConsoleMode = false;
        public void execute(uint addr)
        {
            
            CALL(addr);
            byte opcode = 0;
            while (true)
            {
                try
                {

                    try
                    {
                        InstructionPointer = (int)memory.pos;
                        
                        if (Debug)
                        {
                            InstructionDA = Disassemble();
                            try
                            {  Refresh();
                          
                                this.nextOpcode();
                            }
                            catch (Exception e)
                            {
                                // MessageBox.Show(e.ToString());
                            }
                        }
                        opcode = memory.Read();//Console.WriteLine("INSTRUCTION:" + "0x" + opcode.ToString("X"));
                        Instruction = opcode;
                    }
                    catch
                    {
                        //thrown = true;
                    }
                    
                    if (opcode == Opcodes.PushString)
                    {
                        Stack.Push(memory.ReadString());
                    }
                    else if (opcode == Opcodes.Stptr)
                    {
                        int address = (int)Stack.Pop();
                        if (Stack.Peek() is string)
                        {
                            memory.Write((uint)address, (string)Stack.Peek());
                        }
                        else if (Stack.Peek() is int)
                        {
                            memory.Write((uint)address, (int)Stack.Pop());
                        }
                        else if (Stack.Peek() is byte)
                        {
                            memory.Write((uint)address, (byte)Stack.Pop());
                        }
                    }
                    else if (opcode == Opcodes.Stloc)
                    {
                        int indx = memory.ReadInt32();
                        locals[indx] = null;
                        locals[indx] = new Local();
                        locals[indx].address = vi;
                        if (Stack.Peek() is string)
                        {
                            memory.Write((uint)vi,Stack.Peek().ToString());
                            vi += ((string)Stack.Peek()).Length + 1;
                            locals[indx].Datatype = Stack.Peek();
                        }
                        else if (Stack.Peek() is int)
                        {

                            memory.Write((uint)vi, (int)Stack.Peek());
                            vi += 4;
                            locals[indx].Datatype = Stack.Peek();
                        }
                        else if (Stack.Peek() is byte)
                        {
                            memory.buffer[vi] = (byte)Stack.Peek();
                            vi += 1;
                            locals[indx].Datatype = Stack.Peek();
                        }
                        else
                        {
                            memory.Write((uint)vi, (string)Stack.Peek());
                            vi += ((string)Stack.Peek()).Length + 1;
                            locals[indx].Datatype = Stack.Peek();
                        }
                    }
                    else if (opcode == Opcodes.StlocPtr)
                    {
                        int indx = (int)Stack.Pop();
                        locals[indx].address = locals[memory.ReadInt32()].address ;

                    }
                    else if (opcode == Opcodes.ldptr)
                    {
                        int indx = memory.ReadInt32();
                        Stack.Push(locals[indx].address);

                    }
                    else if (opcode == Opcodes.Ldloc)
                    {
                        int indx = memory.ReadInt32();
                        uint oldp = memory.pos;
                        if (locals[indx].Datatype is string)
                        {
                            memory.pos = (uint)locals[indx].address;
                            Stack.Push(memory.ReadString());
                        }
                        else if (locals[indx].Datatype is int)
                        {
                            memory.pos = (uint)locals[indx].address;
                            Stack.Push(memory.ReadInt32());
                        }
                        else if (locals[indx].Datatype is byte)
                        {
                            memory.pos = (uint)locals[indx].address;
                            Stack.Push((byte)memory.buffer[locals[indx].address]);
                       }
                        memory.pos = oldp;

                    }
                    
                    else if (opcode == Opcodes.LdlocPtr)
                    {
                        int indx = (int)Stack.Pop();
                        Stack.Push(locals[indx]);

                    }
                    else if (opcode == Opcodes.sysf_create_win)
                    {
                        
                        int sizey = (int)Stack.Pop();
                        int sizex = (int)Stack.Pop();
                        string title = (string)Stack.Pop();
                        
                        frm = new vrt.vForm();
                        frm.Size = new System.Drawing.Size(sizex, sizey);
                        frm.Text = title;
                        Thread t = new Thread(create);
                        t.Start();
                         
                    }
                    else if (opcode == Opcodes.sysf_get_msg)
                    {
                        Stack.Push(interceptMessage((string)Stack.Pop()));
                    }
                    else if (opcode == Opcodes.sysf_clr_msg)
                    {
                        foreach (Control c in frm.Controls)
                        {
                            c.Tag = 0;
                        }
                    }
                    else if (opcode == Opcodes.sysf_win_add)
                    {
                        int com_id = (int)Stack.Pop();
                        string com_name = (string)Stack.Pop();
                        int locy = (int)Stack.Pop();
                        int locx = (int)Stack.Pop();
                        int sizey = (int)Stack.Pop();
                        int sizex = (int)Stack.Pop();
                        string name = (string)Stack.Pop();
                        if (com_id == 1) // Label
                        {
                            Label l = new Label();
                            l.Location = new System.Drawing.Point(locx, locy);
                            l.Text = name;
                            l.Name = com_name;
                            l.Click += click;
                            frm.Controls.Add(l);
                        }
                        if (com_id == 2) // Button
                        {
                            Button l = new Button();
                            l.Location = new System.Drawing.Point(locx, locy);
                            l.Text = name;
                            l.Name = com_name;
                            frm.Controls.Add(l);
                            l.Click += click;
                        }
                    }
                    else if (opcode == Opcodes.SysfPrint)
                    {
                        if (Stack.Peek() is string)
                        {
                            if (!redirectOutput)
                                Console.Write(Stack.Peek().ToString());
                            else
                                Output += Stack.Peek();
                        }
                        if (Stack.Peek() is int)
                        {
                            if (!redirectOutput)
                                Console.Write(Stack.Peek().ToString());
                            else
                                Output += Stack.Peek();
                        }
                        else if (Stack.Peek() is byte)
                        {
                            if (!redirectOutput)
                                Console.Write((char)(byte)Stack.Peek());
                            else
                                Output += ((char)(byte)Stack.Peek());
                        }
                    }
                    else if (opcode == Opcodes.Pushbyte)
                    {
                        Stack.Push(memory.Read());
                    }
                    else if (opcode == Opcodes.pushword)
                    {
                        Stack.Push(memory.ReadInt16());
                    }
                    else if (opcode == Opcodes.pushdword)
                    {

                        Stack.Push(memory.ReadInt32());
                    }
                    else if (opcode == Opcodes.stb)
                    {
                        int addrs = memory.ReadInt32() + vstart;
                        string cont = Stack.Peek().ToString();
                        //Console.WriteLine(addrs.ToString());
                        foreach (char b in cont)
                        {
                            memory.Write((uint)addrs, (byte)b);
                            addrs++;
                        }
                        memory.Write((uint)addrs, 0);
                    }
                    
                    else if (opcode == Opcodes.lddword)
                    {
                        int val = 0;
                        byte[] vals = new byte[4];
                        int ptr = memory.ReadInt32() + vstart;
                        for (int i = 0; i < 4; i++)
                        {
                            vals[i] = memory.ReadByte((uint)ptr);
                            vi++;
                        }
                        val = System.BitConverter.ToInt32(vals, 0);
                        Stack.Push(val);
                    }
                    else if (opcode == Opcodes.newobj)
                    {
                        string name = memory.ReadString();
                        string baseClass = memory.ReadString();

                        foreach (vClass c in Classes)
                        {
                            if (c.Name == baseClass)
                            {
                                Instance i = new Instance();
                                i.Name = name;
                                i.Address = vi;
                                loadClass(c);
                               
                                foreach (string s in c.Fields)
                                {
                                    field f = new field();
                                    f.Name = s;
                                    i.fields.Add(f);
                                }
                                i.BaseClass = baseClass;
                                InstanceInfo.Add(i);
                            }
                        }
                    }

                    else if (opcode == Opcodes.Add)
                    {
                        object o1 = Stack.Pop();
                        object o2 = Stack.Pop();
                        
                        if (o1 is byte || o1 is char)
                        {
                            o1 = (string)((char)(byte)o1).ToString();

                        }
                        if (o2 is byte || o2 is char)
                        {
                            o2 = (string)((char)(byte)o2).ToString();
                        }

                        if (o1 is string || o2 is string)
                        {
                            Stack.Push(o2.ToString() + o1.ToString());
                        
                        }
                        else if(o1 is int|| o2 is int)
                        {
                            Stack.Push((int)o2 + (int)o1);
                        }
                    }
                    else if (opcode == Opcodes.Mul)
                    {
                        object o1 = Stack.Pop();
                        object o2 = Stack.Pop();
                        Stack.Push((int)o2 * (int)o1);
                    }
                    else if (opcode == Opcodes.Sub)
                    {
                        object o1 = Stack.Pop();
                        object o2 = Stack.Pop();
                        Stack.Push((int)o2 - (int)o1);
                    }
                    else if (opcode == Opcodes.Div)
                    {
                        object o1 = Stack.Pop();
                        object o2 = Stack.Pop();
                        Stack.Push((int)o2 / (int)o1);
                    }
                    else if (opcode == Opcodes.Call)
                    {
                        string name = memory.ReadString();
                        uint faddr = (uint)memory.ReadInt32();
                        foreach (classInformation ci in info)
                        {
                            if (ci.Name == name)
                            {
                                CALL((faddr + ci.Address), name);
                                break;
                            }
                        }
                    }
                    else if (opcode == Opcodes.call_instance)
                    {
                        string name = memory.ReadString();
                        uint faddr = (uint)memory.ReadInt32();
                        if (name == "this")
                        {
                            foreach (Instance ci in InstanceInfo)
                            {
                                if (ci.Address == CallStack.Peek().instancePos)
                                {
                                    name = ci.Name;
                                }
                            }
                        }
                        foreach (Instance ci in InstanceInfo)
                        {
                            if (ci.Name == name)
                            {
                                CALL(faddr + (uint)ci.Address, name);
                            }
                        }
                    }
                    else if (opcode == Opcodes.byte2int)
                    {
                        byte b = (byte)Stack.Pop();
                        Stack.Push(Convert.ToByte(b));
                    }
                    else if (opcode == Opcodes.Set)
                    {
                        string inst = memory.ReadString();
                        string name = memory.ReadString();
                        if (inst == "this")
                        {
                            foreach (Instance ci in InstanceInfo)
                            {
                                if (ci.Address == CallStack.Peek().instancePos)
                                {
                                    inst = ci.Name;
                                }
                            }
                        }
                        foreach (Instance ci in InstanceInfo)
                        {
                            if (ci.Name == inst)
                            {
                                foreach (field f in ci.fields)
                                {
                                    if (f.Name == name)
                                    {

                                        f.Value = Stack.Pop();
                                    }
                                }
                                break;
                            }
                        }

                    }
                    else if (opcode == Opcodes.Get)
                    {
                        string inst = memory.ReadString();
                        string name = memory.ReadString();
                        if (inst == "this")
                        {
                            foreach (Instance ci in InstanceInfo)
                            {
                                if (ci.Address == CallStack.Peek().instancePos)
                                {
                                    inst = ci.Name;
                                }
                            }
                        }
                        foreach (Instance ci in InstanceInfo)
                        {
                            if (ci.Name == inst)
                            {
                                foreach (field f in ci.fields)
                                {
                                    if (f.Name == name)
                                    {
                                        Stack.Push(f.Value);
                                    }
                                }
                                break;
                            }
                        }

                    }
                    else if (opcode == Opcodes.Ldobj)
                    {
                        string name = memory.ReadString();
                        Instance ci = (Instance)Stack.Peek();
                        ci.Address = vi;
                        ci.Name = name;
                        foreach (vClass c in Classes)
                        {
                            if (c.Name == ci.Name)
                            {
                                loadClass(c);
                            }
                        }
                        InstanceInfo.Add(ci);
                    }
                    else if (opcode == Opcodes.Pushobj)
                    {
                        string inst = memory.ReadString();
                        foreach (Instance ci in InstanceInfo)
                        {
                            if (ci.Name == inst)
                            {
                                Stack.Push(ci);
                            }
                        }
                    }
                    else if (opcode == Opcodes.Jmp)
                    {
                        uint addp = CallStack.Peek().instancePos;

                        memory.pos = CallStack.Peek().instancePos + ((uint)memory.ReadInt32());

                    }
                    else if (opcode == Opcodes.Read)
                    {
                        if (!redirectOutput)
                        {
                            Stack.Push(Console.ReadLine());
                        }
                        else
                        {
                            ExceptInput = true;
                            while (ExceptInput)
                            {
                            }
                            Stack.Push(InputBuffer);
                            InputBuffer = "";
                        }
                    }
                    else if (opcode == Opcodes.Bre)
                    {
                        System.Collections.Generic.Stack<Object> old = Stack;
                        object o1 = Stack.Pop();
                        object o2 = Stack.Pop();
                        if (o1.ToString() == o2.ToString())
                        {
                            memory.pos = CallStack.Peek().instancePos + ((uint)memory.ReadInt32());

                        }
                        Stack = old;
                    }
                    else if (opcode == Opcodes.Bne)
                    {
                        System.Collections.Generic.Stack<Object> old = Stack;
                        object o1 = Stack.Pop();
                        object o2 = Stack.Pop();

                        if (o1.ToString() != o2.ToString())
                        {
                            memory.pos = CallStack.Peek().instancePos + ((uint)memory.ReadInt32());

                        }
                        else
                        {
                            Stack = old;
                        }
                    }
                    else if (opcode == Opcodes.Blt)
                    {
                        System.Collections.Generic.Stack<Object> old = Stack;
                        object o1 = Stack.Pop();
                        object o2 = Stack.Pop();

                        if ((int)o1 < (int)o2)
                        {
                            memory.pos = CallStack.Peek().instancePos + ((uint)memory.ReadInt32());

                        }
                        else
                        {
                            Stack = old;
                        }
                    }
                    else if (opcode == Opcodes.Bgt)
                    {
                        System.Collections.Generic.Stack<Object> old = Stack;
                        object o1 = Stack.Pop();
                        object o2 = Stack.Pop();
                        if ((int)o1 > (int)o2)
                        {
                            memory.pos = CallStack.Peek().instancePos + ((uint)memory.ReadInt32());


                        }
                        else
                        {
                            Stack = old;
                        }
                    }
                    else if (opcode == Opcodes.Cmpgt)
                    {
                        if ((int)Stack.Pop() > (int)Stack.Pop())
                        {
                            Stack.Push(1);
                        }
                        else
                        {
                            Stack.Push(0);
                        }
                    }
                    else if (opcode == Opcodes.Cmplt)
                    {
                        if ((int)Stack.Pop() < (int)Stack.Pop())
                        {
                            Stack.Push(1);
                        }
                        else
                        {
                            Stack.Push(0);
                        }
                    }
                    else if (opcode == Opcodes.readarr)
                    {
                        int address = (int)Stack.Pop();
                        Stack.Push(memory.ReadByte((uint)address));
                    }

                    else if (opcode == Opcodes.Resb)
                    {
                        int size = memory.ReadInt32();
                        vi += size;
                    }
                    else if (opcode == Opcodes.val2str)
                    {
                        if (Stack.Peek() is byte)
                        {

                            // Stack.Push(((char)(byte)Stack.Pop()).ToString());
                        }
                    }
                    else if (opcode == Opcodes.Try)
                    {
                        Exceptionstack.Push((uint)memory.ReadInt32());
                    }
                    else if (opcode == Opcodes.Catch)
                    {
                        memory.pos = (uint)memory.ReadInt32();
                    }
                    else if (opcode == Opcodes.Halt)
                    {
                        Console.WriteLine("WTF lawl");
                        Console.ReadKey();
                    }

                    else if (opcode == Opcodes.And)
                    {
                        bool i1, i2;
                        i1 = Convert.ToBoolean((int)Stack.Pop());
                        i2 = Convert.ToBoolean((int)Stack.Pop());
                        bool t = i1 && i2;
                        Stack.Push(Convert.ToInt32(t));
                    }
                    else if (opcode == Opcodes.Exnor)
                    {
                        if (Stack.Pop().ToString() == Stack.Pop().ToString())
                        {
                            Stack.Push(1);
                        }
                        else
                        {
                            Stack.Push(0);
                        }
                    }
                    else if (opcode == Opcodes.Or)
                    {
                        bool i1, i2;
                        i1 = Convert.ToBoolean((int)Stack.Pop());
                        i2 = Convert.ToBoolean((int)Stack.Pop());
                        bool t = i1 || i2;
                        Stack.Push(Convert.ToInt32(t));
                    }
                    else if (opcode == Opcodes.Not)
                    {
                        bool i1;
                        i1 = Convert.ToBoolean((int)Stack.Pop());
                        bool t = !i1;
                        Stack.Push(Convert.ToByte(t));
                    }
                    else if (opcode == Opcodes.Throw)
                    {
                        thrown = true;
                        bad_oc = opcode;
                        msg = Stack.Pop().ToString();
                    }
                    else if (opcode == Opcodes.Pop)
                    {
                        Stack.Pop();
                    }
                    else if (opcode == Opcodes.Ret)
                    {
                        if (CallStack.Count > 1)
                        {
                            call c = CallStack.Pop();


                            memory.pos = c.returnaddress;
                        }
                        else
                        {
                            return;
                        }

                    }
                    if (Exceptionstack.Count < 1 && thrown)
                    {
                        Console.Clear();
                        Console.WriteLine("          *** Unhandled Exception! ***");
                        Console.WriteLine("A problem has occured in the current application");
                        Console.WriteLine("Instruction: 0x" + bad_oc.ToString("X"));
                        Console.WriteLine("ADDRESS: 0x" + memory.pos.ToString("X"));
                        Console.WriteLine("INFORMATION:" + msg);
                        Console.WriteLine("-------- CALLSTACK --------");
                        foreach (call c in CallStack)
                        {
                            Console.Write("AT: 0x" + c.address.ToString("X"));
                            //Console.WriteLine("(" + ASM.GetAssemblyName((int)c.address) + ")");
                        }
                        Console.ReadKey();
                        return;
                    }
                    else if (thrown)
                    {
                        memory.pos = Exceptionstack.Pop();
                        thrown = false;
                    }
                }
                catch (Exception e)
                {



                }
            }

        }

        private class classInformation
        {
            public string Name;
            public uint Address;
            
        }
        private List<Instance> InstanceInfo = new List<Instance>();
        public GEX gexfile;
        private Assembly ASM;
        private List<classInformation> info = new List<classInformation>();
        private List<vClass> Classes = new List<vClass>();
        public void loadClass(vClass CLASS)
        {
            foreach (byte b in CLASS.generateByteCode())
            {
                memory.Write((uint)vi, b);
                vi++;
            }
         
        }
        public bool ExceptInput = false;
        public string InputBuffer = "";
        public void Run(Assembly asm)
        {
            
            vClass program;
            uint program_start = (uint)asm.Entrypoint;
            string name = null;
            Classes = asm.Classes;
            Array.Resize<byte>(ref memory.buffer, 64000);

            foreach (vClass v in asm.Classes)
            {
                
                if (v.Name.ToLower() == "program")
                {
                    classInformation ci = new classInformation();
                    ci.Name = v.Name;
                    ci.Address = (uint)vi;
                    info.Add(ci);
                    program = v;
                    program_start = v.CodeStart;
                    loadClass(v);
                    name = v.Name;
                }
            }
            foreach (vClass v in asm.Classes)
            {
                if (v.Name.ToLower() != "program")
                {
                    classInformation ci = new classInformation();
                    ci.Name = v.Name;
                    ci.Address = (uint)vi;
                    info.Add(ci);
                    loadClass(v);
                }
            }
            
            vstart = vi;
            execute(program_start);
           // Console.WriteLine("Break at:" + this.memory.pos.ToString());
            Console.ReadLine();
        }


        public void LoadGEX(string GEXFile)
        {
            Assembly a = new Assembly();

           // a.Load(GEXFile);

        }

        //   [STAThread]

        public void AssembleGEX(string code)
        {

        }

        public int start = 0;


    }

}
